package de.gameduell.framework.game.card {
	import de.gameduell.framework.debug.Debug;
	import de.gameduell.framework.game.card.display.IDisplayMode;
	import de.gameduell.framework.game.card.display.Stack;
	import de.gameduell.framework.game.card.sort.ISortMode;
	import de.gameduell.framework.gui.wrapper.SpriteWrapper;
	
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.geom.Point;	

	public class CardStack extends SpriteWrapper {

		private var _cards:Array;
		private var _rotation:Number;
		private var _active:Boolean;
		private var _sortMode:ISortMode;
		private var _displayMode:IDisplayMode;
		private var _identity:Number;
		private var _type:Number;
		private var _scaleFactor:Number;
		
		protected var _faceDown:Boolean;
		
		/**  
		 * @param pos    = position of the cardstack
		 * @param rotate = rotation of the stack
		 * @param scale  = scale of the stack
		**/
		public function CardStack(pos:Point, rotate:int = 0, scale:Number = 1) {
			super(new Sprite());
			position  = pos; 			
			_cards    = new Array();
			_rotation = rotate;
			_displayMode = new Stack(new Point(0, 20));
			_sortMode = null;
			_scaleFactor = scale;
			_faceDown    = false;
			addEventListener(Event.ADDED_TO_STAGE, initClip);
		}
		
		private function initClip(event:Event):void {
			removeEventListener(Event.ADDED_TO_STAGE, initClip);
			rotation = _rotation;
			position = _position;
		}
		
		/**  
		 * @param isActive = activates or deactivates the cardstack
		**/
		public function set active(isActive:Boolean):void {
			_active       = isActive;
			mouseEnabled  = _active;
			mouseChildren = _active;
		}
		
		/**  
		 * @param card = add a card to the stack
		**/
		public function addCard( card:Card ):void {
			addChild(card.sprite);
			card.stack        = this;
			card.index        = _cards.length;
			card.rotation 	  = 0;
			card.mouseEnabled = true;
			card.doubleClickEnabled = true;
			card.addEventListener(CardEvent.CLICK, cardClicked);
			card.addEventListener(CardEvent.MOUSE_DOWN, cardClicked);
			card.addEventListener(CardEvent.MOUSE_UP, cardClicked);
			card.flip = _faceDown;	
			_cards.push(card);
			updateDisplay();
		}
		
		public function replaceCardAt(index:int,newCard:Card):Card{
			var replacedCard:Card = getCardByIndex(index);
			addChild(newCard.sprite);
			newCard.stack        = this;
			newCard.index        = _cards.length;
			newCard.rotation 	  = 0;
			newCard.mouseEnabled = true;
			newCard.doubleClickEnabled = true;
			newCard.addEventListener(CardEvent.CLICK, cardClicked);
			newCard.addEventListener(CardEvent.MOUSE_DOWN, cardClicked);
			newCard.addEventListener(CardEvent.MOUSE_UP, cardClicked);
			newCard.flip = _faceDown;	
			_cards[index] = newCard;
			removeCard(replacedCard);
			return replacedCard;
		}

		/**
		 * the card must be removed from cards array before calling this method!
		 **/		
		protected function removeCard( card:Card ):void {
			removeChild(card.sprite);
			card.removeEventListener(CardEvent.CLICK, cardClicked);
			card.removeEventListener(CardEvent.MOUSE_DOWN, cardClicked);
			card.removeEventListener(CardEvent.MOUSE_UP, cardClicked);
			updateDisplay();
		}
		
		/**  
		 * @param aCards = add a whole array of cards to the stack
		**/
		public function addCards(aCards:Array):void{
			for(var i:* in aCards){
				addCard(Card(aCards[i]));
			}
		}
		
		private function cardClicked(event:CardEvent):void {
			dispatchEvent(new CardEvent(event.card, event.type));
		}
		
		/**  
		 * @return Card = the top card of the cardstack
		**/
		public function getTopCard():Card {
			return _cards[_cards.length-1];		
		}
		
		/**  
		 * @param index = the index of the card that is returned
		 * @return Card = the returned card
		**/
		public function getCardByIndex(index:int):Card{
			return _cards[index];
		}
		
		/**  
		 * @param aCardString = the identifier of the card that is returned
		 * @return Card = the returned card
		**/
		public function getCardByString(aCardString:String):Card{
			var index:int     = -1;
			var tempCard:Card = null;
			
			for(var i:* in _cards){
				if(_cards[i].cardString == aCardString){
					index = _cards[i].index;
					break;
				}
			}
			if(index != -1){
				tempCard = _cards[index];
			}
			
			return tempCard;
		}

		/**  
		 * @return card = the top card is returned and removed from the stack
		**/
		public function takeTopCard():Card {
			var card:Card = Card(_cards.pop());
			removeCard(card);
			return card;		
		}
		
		/**  
		 * @return card = the bottom card is returned and removed from the stack
		**/
		public function takeBottomCard():Card {
			var card:Card = Card(_cards.shift());
			removeCard(card);
			return card;		
		}
		
		/**  
		 * @param aCard = the card that is to be removed from the stack and returned
		 * @return card = the returned card
		**/
		public function takeCard(aCard:Card):Card{
			var tempCard:Card;	
			var index:int = aCard.index;
			tempCard = _cards[index];
			_cards.splice(index, 1);
			removeCard(tempCard);
			return tempCard;
		}
		
		/**  
		 * @param aCardString = the identifier of the card that is to be removed from the stack and returned
		 * @return tempCard = the returned card
		**/
		public function takeCardByString(aCardString:String):Card{
			//TODO check this works in MAU??
			var tempCard:Card = getCardByString(aCardString);
			if(tempCard != null){
				_cards.splice(tempCard.index-1, 1); // TODO ???
				removeCard(tempCard);
			}
			return tempCard;
		}
		
		/**  
		 * the index of the cards of the stack is updated
		**/
		public function updateCardIndex():void {
			for(var i:Number = 0;i < _cards.length;i++) {
				_cards[i].index = i;
			}
		}
		
		/**  
		 * empties the stack
		**/
		public function clear():void {
			_cards = new Array();
			while (numChildren > 0) {
				this.removeChild(this.getChildAt(0));
			}
		}
		
		/**  
		 * orders the stack
		**/
		public function updateDisplay():void {
			if(	_sortMode != null) {
				_sortMode.sortCards(this);
			}
			updateCardIndex();
			if(	_displayMode != null) {
				_displayMode.updateDisplay(this);	
			}						
		}
		
		public function sortCards(reverseSort:Boolean = false):void {
			_sortMode.sortReverse(reverseSort);
			updateDisplay();
		}
		
		/**  
		 * @return Number = the number of the cards in the stack
		**/
		public function get size():Number {
			return _cards.length;
		}
		
		/**  
		 * @return Number = the factor the stack is scaled about
		**/
		public function get scaleFactor():Number{
			return _scaleFactor;
		}
		
		/**  
		 * @return Array = the cards of the stack
		**/
		public function get cards():Array {
			return _cards;
		}
		
		/**  
		 * @return Number = the identity of the stack
		**/
		public function get identity():Number {
			return _identity;
		}
		
		/**  
		 * @return IDisplayMode = the displaymode of the stack
		**/
		public function get displayMode():IDisplayMode {
			return _displayMode;
		}
		
		/**  
		 * @return if the stack is active
		**/
		public function get active():Boolean{
			return _active;
		}
	
		/**  
		 * @param mode = the mode the stack is sorted after
		**/
		public function set sortMode( mode:ISortMode ):void {
			_sortMode = mode;
			updateDisplay();
		}
		
		/**  
		 * @param mode = how the stack is displayed
		**/
		public function set displayMode( mode:IDisplayMode ):void {
			_displayMode = mode;
			updateDisplay();
		}
		
		/**  
		 * @param newIdentity = the identity of the stack
		**/
		public function set identity(newIdentity:Number):void {
			_identity = newIdentity;
		}
		
		/**  
		 * @param newType = the type of the stack
		**/
		public function set type(newType:Number):void {
			_type = newType;
		}
		
		/**  
		 * @param newFactor = the factor the stack is scaled about
		**/
		public function set scaleFactor(newFactor:Number):void{
			_scaleFactor = newFactor;
			updateDisplay();
		}
		
		/**  
		 * @param isFaceDown = is the front of the stack visible or the back
		**/
		public function set faceDown(isFaceDown:Boolean):void{
			_faceDown = isFaceDown;
			for(var i:* in _cards){
				_cards[i].flip = isFaceDown;
			}
		}
		
		/**  
		 * @param newRotation = the rotation of the stack
		**/
		override public function set rotation(newRotation:Number):void{
			super.rotation = newRotation;
			updateDisplay();
		}
	}
}
